package GMDPRE

var UserA User
var UserB User

type User struct {
	// 公开数据
	id BN
	pk AffPoint
	// 隐私数据
	priKey BN
	KO     []uint8
}

func (u *User) GetId() *BN {
	return &u.id
}

func (u *User) getPriKey() *BN {
	return &u.priKey
}

func (u *User) GetPubKey() *AffPoint {
	return &u.pk
}

func (u *User) GetPubKeyBytes() []uint8 {
	return AffP2Bytes(&u.pk)
}

func (u *User) Init() {
	//if BnCmp(&u.priKey, &Bn0, Sm2CurveDigit) != 0 {
	//	return
	//}

	GenBnLessN(&u.id)
	GenBnLessN(&u.priKey)
	NumMulG2AffC(&u.pk.x, &u.pk.y, &u.priKey)
}

// ReKeyGen 为userB生成重加密密钥
// KF 是密钥片段集合，其中的元素是Frag字节数组
// PubKeyBytes是userB的公钥，大小为64字节
// idOfPN 是各个代理节点的id的数组，数组长度为NumOfFrag，每个id是长度为32字节
func (u *User) ReKeyGen(KF *[NumOfFrag][]uint8, PubKeyBytes []uint8, idOfPN *[NumOfFrag][]uint8) {
	var i int
	var bnTmp1, bnTmp2 BN
	var apTmp1 AffPoint
	var jpTmp1 JacPoint
	var ctx Sm3Ctx
	var pubKeyOfB AffPoint

	Bytes2AffP(&pubKeyOfB, PubKeyBytes)

	// 选择随机数$x_{A}$
	GenBnLessN(&bnTmp1)

	var XA AffPoint
	NumMulG2AffP(&XA, &bnTmp1)                 // 6次模乘，2次求逆，1次点乘
	AffP2JacP(&jpTmp1, &pubKeyOfB)             // PK_B as Mont Jac Point，  // 2次模乘
	NumMulJacP2AffP(&apTmp1, &bnTmp1, &jpTmp1) // [x]PK_B as Aff Point		// 6次模乘，2次求逆，1次点乘
	// H3
	Sm3Init(&ctx)
	Sm3Update(&ctx, AffP2Bytes(&XA), Sm2AffPointSize)               // X_A
	Sm3Update(&ctx, PubKeyBytes, Sm2AffPointSize)                   // PK_B
	Sm3Update(&ctx, AffP2Bytes(&apTmp1), Sm2AffPointSize)           // [x_A]PK_B
	BytesToBn(&bnTmp1, Sm2CurveDigit, Sm3Finish(&ctx), Sm3HashSize) // 1次哈希

	// Construct f(x) = f_0 + f_1 * x + f_2 * x^2 + f_3 * x^3 + f_4 * x^4
	var p Poly
	// d enter mont area
	BnMontMul(&bnTmp1, &bnTmp1, &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模乘
	// d ^ (-1) % n
	BnMontExp(&p.f[0], &bnTmp1, &Sm2NSub2, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模逆
	// f_0 = a * d ^(-1) % n (in mont area)
	// a enter mont area
	BnMontMul(&bnTmp1, u.getPriKey(), &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模乘
	BnMontMul(&p.f[0], &bnTmp1, &p.f[0], &Sm2N, Sm2McN, Sm2CurveDigit)       // 1次模乘
	// f_1, f_2, f_3, f_4, assuming that they have already in mont area
	for i = 1; i < Threshold; i++ {
		GenBnLessN(&p.f[i])
	}

	// D = H6(PK_A, PK_B, [a]PK_B)
	NumMulJacP2AffP(&apTmp1, u.getPriKey(), &jpTmp1) // [a]PK_B as Aff Point // 6次模乘，2次求逆，1次点乘
	Sm3Init(&ctx)
	Sm3Update(&ctx, u.GetPubKeyBytes(), Sm2AffPointSize)  // X_A
	Sm3Update(&ctx, PubKeyBytes, Sm2AffPointSize)         // PK_B
	Sm3Update(&ctx, AffP2Bytes(&apTmp1), Sm2AffPointSize) // [a]PK_B
	var D = Sm3Finish(&ctx)                               // 1次哈希

	var frag Frag
	for i = 0; i < NumOfFrag; i++ { // 6N+2tN次模乘，2N次求逆，N次点乘，N次哈希
		// y
		GenBnLessN(&bnTmp1)
		frag.id = idOfPN[i]
		// sx = H5(id, D) = bnTmp2
		Sm3Init(&ctx)
		Sm3Update(&ctx, idOfPN[i], Sm2CurveSize) // ID_i
		Sm3Update(&ctx, D, Sm3HashSize)          // D
		BytesToBn(&bnTmp2, Sm2CurveDigit, Sm3Finish(&ctx), Sm3HashSize)
		BnMod(&bnTmp2, &bnTmp2, &Sm2N, Sm2CurveDigit) // 1次哈希，已汇总
		// Y = [y]G
		NumMulG2AffP(&apTmp1, &bnTmp1) // 6次模乘，2次求逆，1次点乘，已汇总
		// rk = f(sx)
		p.eval(&frag.rk, &bnTmp2, &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit) // 2t次模乘，已汇总
		// XA
		frag.XA = XA

		KF[i] = frag.toBytes()
	}
}

// EnCapsulate 封装密钥
// 20次模乘，6次模逆，3次点乘，1次哈希，1次签名
func (u *User) EnCapsulate() []uint8 {
	var rr, tmp BN
	var ctx Sm3Ctx
	var capTmp Capsule
	var KO AffPoint

	GenBnLessN(&rr)
	//GenBnLessN(&uu)

	// E = [r]G, V = [u]G
	NumMulJacP2AffP(&capTmp.E, &rr, &Sm2JacG) // 6次模乘，2次求逆，1次点乘
	//NumMulJacP2AffP(&capTmp.V, &uu, &Sm2JacG) // 6次模乘，2次求逆，1次点乘
	// K = KDF(KO), KO = a(E+V)=(r+u)PK_A
	tmp = rr
	//BnModAdd(&tmp, &rr, &uu, &Sm2N, Sm2CurveDigit)
	NumMulAffP2AffP(&KO, &tmp, u.GetPubKey()) // 8次模乘，2次求逆，1次点乘
	u.KO = AffP2Bytes(&KO)

	// s = Sm2SignHash((E,V))
	Sm3Init(&ctx)
	Sm3Update(&ctx, AffP2Bytes(&capTmp.E), Sm2AffPointSize)
	//Sm3Update(&ctx, AffP2Bytes(&capTmp.V), Sm2AffPointSize)
	var h = Sm3Finish(&ctx) // 1次哈希

	var ret int // 1次签名
	for {
		GenBnLessN(&tmp)
		ret, capTmp.s = Sm2SignHash(BnToBytes(&u.priKey, Sm2CurveDigit), BnToBytes(&tmp, Sm2CurveDigit), h)
		if ret == 0 {
			break
		}
	}

	return capTmp.toBytes()
}

// DeCapsulate 通过E和V恢复出密钥，不应计入总时间
// 10次模乘，2次求逆，1次点乘，1次点加
func (u *User) DeCapsulate(cBytes []uint8) []uint8 {
	var EJacP JacPoint
	//var VJacP JacPoint
	var ap AffPoint
	var capTmp Capsule

	capTmp.fromBytes(cBytes)

	AffP2JacP(&EJacP, &capTmp.E) // 2次模乘
	//AffP2JacP(&VJacP, &capTmp.V)                // 2次模乘
	//JacPAddJacP2JacP(&EJacP, &EJacP, &VJacP)    // 1次点加
	NumMulJacP2AffP(&ap, u.getPriKey(), &EJacP) // 6次模乘，2次求逆，1次点乘
	return AffP2Bytes(&ap)
}

// DeCapsulateFrags 根据t个重加密片段，还原出对称密钥
// 8+t+(2(t-1)+6)t+8+6=22+5t+2t^2次模乘，2+t+2+2=6+t次模逆，1+2t+1+1=3+2t次点乘，1+2t次点加，1+t+1=2+t次哈希
func (u *User) DeCapsulateFrags(PubKeyBytes []uint8, cF [][]uint8) []uint8 {
	if len(PubKeyBytes) != Sm2AffPointSize || len(cF) < Threshold {
		return []uint8{}
	}

	var i, j int
	var CKO, apTmp1 AffPoint
	var ctx Sm3Ctx

	var ap AffPoint
	Bytes2AffP(&ap, PubKeyBytes)

	NumMulAffP2AffP(&apTmp1, u.getPriKey(), &ap) // 8次模乘，2次求逆，1次点乘

	Sm3Init(&ctx)
	Sm3Update(&ctx, PubKeyBytes, Sm2AffPointSize)
	Sm3Update(&ctx, u.GetPubKeyBytes(), Sm2AffPointSize)
	Sm3Update(&ctx, AffP2Bytes(&apTmp1), Sm2AffPointSize)
	var D = Sm3Finish(&ctx) // 1次哈希

	var sxs [NumOfFrag]BN
	var cFs = make([]CFrag, Threshold)
	for i = 0; i < Threshold; i++ { // t次模乘，t次哈希
		cFs[i].fromBytes(cF[i])

		Sm3Init(&ctx)
		Sm3Update(&ctx, cFs[i].id, Sm2CurveSize)
		Sm3Update(&ctx, D, Sm3HashSize)
		BytesToBn(&sxs[i], Sm2CurveDigit, Sm3Finish(&ctx), Sm3HashSize) // 1次哈希，已汇总
		BnMod(&sxs[i], &sxs[i], &Sm2N, Sm2CurveDigit)
		// sx_i enter mont area
		BnMontMul(&sxs[i], &sxs[i], &Sm2RRN, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模乘，已汇总
	}

	var E = JacPoint{BN{1}, BN{1}, BN{0}}
	//var V = JacPoint{BN{1}, BN{1}, BN{0}}
	var tmp JacPoint
	var bnTmp1, bnTmp2, bnTmp3 BN

	for i = 0; i < Threshold; i++ { // (2(t-1)+6)t次模乘，t次求逆，2t次点乘，2t次点加
		bnTmp1 = Sm2Mont1N
		bnTmp2 = Sm2Mont1N
		for j = 0; j < Threshold; j++ { // 2(t-1)次模乘，已汇总
			if j == i {
				continue
			}
			// sx[j]
			BnMontMul(&bnTmp1, &bnTmp1, &sxs[j], &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模乘，已汇总
			// sx[j] - sx[i]
			BnModSub(&bnTmp3, &sxs[j], &sxs[i], &Sm2N, Sm2CurveDigit)
			BnMontMul(&bnTmp2, &bnTmp2, &bnTmp3, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模乘，已汇总
		}
		// 分母求逆
		BnMontExp(&bnTmp2, &bnTmp2, &Sm2NSub2, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次求逆，已汇总
		// lambda_i
		BnMontMul(&bnTmp1, &bnTmp1, &bnTmp2, &Sm2N, Sm2McN, Sm2CurveDigit) // 1次模乘，已汇总
		BnMontRdc(&bnTmp1, &bnTmp1, &Sm2N, Sm2McN, Sm2CurveDigit)          // 1次模乘，已汇总

		AffP2JacP(&tmp, &cFs[i].E1)          // 2次模乘，已汇总
		NumMulJacP2JacP(&tmp, &bnTmp1, &tmp) // 1次点乘，已汇总
		JacPAddJacP2JacP(&E, &tmp, &E)       // 1次点加，已汇总

		//AffP2JacP(&tmp, &cFs[i].V1)          // 2次模乘，已汇总
		//NumMulJacP2JacP(&tmp, &bnTmp1, &tmp) // 1次点乘，已汇总
		//JacPAddJacP2JacP(&V, &tmp, &V)       // 1次点加，已汇总
	}

	var d BN
	// [b]X_A
	NumMulAffP2AffP(&apTmp1, u.getPriKey(), &cFs[0].XA) // 8次模乘，2次求逆，1次点乘
	Sm3Init(&ctx)
	Sm3Update(&ctx, AffP2Bytes(&cFs[0].XA), Sm2AffPointSize) // X_A
	Sm3Update(&ctx, u.GetPubKeyBytes(), Sm2AffPointSize)     // PK_B
	Sm3Update(&ctx, AffP2Bytes(&apTmp1), Sm2AffPointSize)
	BytesToBn(&d, Sm2CurveDigit, Sm3Finish(&ctx), Sm3HashSize) // 1次哈希
	// d = H3(X_A, PK_B, [b]X_A)
	BnMod(&d, &d, &Sm2N, Sm2CurveDigit)

	//JacPAddJacP2JacP(&E, &E, &V)  // 1次点加
	NumMulJacP2AffP(&CKO, &d, &E) // 6次模乘，2次求逆，1次点乘

	return AffP2Bytes(&CKO)
}
